package view;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import engine.Parameter;
import engine.Parameters;
import model.Model;
import utils.NumericField;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.FileChooser;

/**
 * User interface tab for parameters display and tweaking.
 *
 * <p>The current Model is instantiated by this class.</p>
 *
 * @author Ricardo Cruz {@literal <ricardo.pdm.cruz@gmail.com>}
 * @since  2014-11-12
 */
public class TabParameters extends Tab
{
	private VBox vbox;
	private Label fileLabel;
	private HBox fileBox;
	private TextArea modelText;

	public TabParameters(Window window) {
		super("Parameters");
		setClosable(false);

		fileBox = new HBox(4);
		fileLabel = new Label();
		fileLabel.setFont(Font.font(null, FontPosture.ITALIC, 8));
		fileLabel.setAlignment(Pos.CENTER_LEFT);
		fileLabel.setMaxHeight(Double.POSITIVE_INFINITY);
		Button open = new Button("Open");
		Button save = new Button("Save");
		FileChooser chooser = new FileChooser();
		open.setOnAction(ev -> {
			read(chooser.showOpenDialog(open.getScene().getWindow()));
		});
		save.setOnAction(ev -> {
			write(chooser.showSaveDialog(save.getScene().getWindow()));
		});
		fileBox.getChildren().addAll(open, save, fileLabel);
		fileBox.setPadding(new Insets(0,0,0,4));

		vbox = new VBox(6);
		populate();

		HBox modelBox = new HBox(12);
		Label label = new Label("Model:");
		label.setAlignment(Pos.CENTER_LEFT);
		label.setMaxHeight(Double.POSITIVE_INFINITY);
		modelBox.getChildren().addAll(label, new ModelComboBox(window));

		modelText = new TextArea(Model.description(Model.getCurrentModel()));
		modelText.setWrapText(true);
		modelText.setEditable(false);
		modelText.setPrefHeight(60);

		VBox box = new VBox(6);
		box.setPadding(new Insets(6));
		box.getChildren().addAll(modelBox, modelText, vbox, fileBox);
		ScrollPane scroll = new ScrollPane(box);
		scroll.setHbarPolicy(ScrollBarPolicy.NEVER);
		scroll.setPrefHeight(0);
		setContent(scroll);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Control createControl(Parameter p) {
		NumericField field = new NumericField(p.get().doubleValue());
		field.textProperty().addListener((observable, oldValue, newValue) -> {
			try {
				p.set(Double.parseDouble(newValue));
			} catch(Exception ex) {}
		});
		return field;
	}

	@SuppressWarnings("rawtypes")
	private void populate() {
		vbox.getChildren().clear();
		for(Parameter p: Parameters.getParameters()) {
			if(p.getModel() >= 0 && p.getModel() != Model.getCurrentModel())
				continue;
			GridPane grid = null;
			for(javafx.scene.Node n: vbox.getChildren()) {
				TitledPane tp = (TitledPane) n;
				if(tp.getText().equals(p.getType()))
					grid = (GridPane) tp.getContent();
			}
			if(grid == null) {
				grid = new GridPane();
				grid.setHgap(12);
				grid.setVgap(4);
				TitledPane tp = new TitledPane(p.getType(), grid);
				tp.setAnimated(false);
				vbox.getChildren().add(tp);
			}
			Control c = createControl(p);
			int row = grid.getChildren().size()/2;
			grid.add(new Label(p.getName()), 0, row);
			grid.add(c, 1, row);
			GridPane.setHgrow(c, Priority.ALWAYS);
		}
		fileBox.setVisible(!vbox.getChildren().isEmpty());
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void read(File file) {
		if(file == null) return;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line;
			while(true) {
				line = reader.readLine();
				if(line == null) break;
				int i = line.indexOf('=');
				String name = line.substring(0,i);
				String value = line.substring(i+1);
				for(Parameter p: Parameters.getParameters()) {
					if(name.equals(p.getType()+"-"+p.getName()))
						p.set(Double.valueOf(value));
				}
			}
			reader.close();
			fileLabel.setText(file.getAbsolutePath());
			populate();
		} catch(Exception ex) {
			fileLabel.setText("Error: could not read from\n" + file.getAbsolutePath());
		}
	}

	@SuppressWarnings("rawtypes")
	private void write(File file) {
		if(file == null) return;
		try {
			PrintWriter writer = new PrintWriter(file);
			for(Parameter p: Parameters.getParameters())
				writer.println(p.getType()+"-"+p.getName() + "=" + p.get());
			writer.close();
			fileLabel.setText(file.getAbsolutePath());
		} catch(Exception ex) {
			fileLabel.setText("Error: could not write to\n" + file.getAbsolutePath());
		}
	}

	private class ModelComboBox extends ComboBox<String> {
		public ModelComboBox(Window window) {
			super();
			for(int i = 0; i < Model.getNbrModels(); i++)
				getItems().add(Model.getModelName(i));
			getSelectionModel().select(Model.getCurrentModel());
			getSelectionModel().selectedIndexProperty().addListener(
				(ObservableValue<? extends Number> observable, Number old,
				Number value) -> {
					window.setModel(value.intValue());
					modelText.setText(Model.description(Model.getCurrentModel()));
					populate();
			});
		}
	}
}
